1、题干
给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能从 s 获得的 有效 IP 地址 。你可以按任何顺序返回答案。
有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。
例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。
示例 1:
输入:s = "25525511135"
输出:["255.255.11.135","255.255.111.35"]
示例 2:
输入:s = "0000"
输出:["0.0.0.0"]
示例 3:
输入:s = "1111"
输出:["1.1.1.1"]
示例 4:
输入:s = "010010"
输出:["0.10.0.10","0.100.1.0"]
示例 5:
输入:s = "10203040"
输出:["10.20.30.40","102.0.30.40","10.203.0.40"]
提示:
0 <= s.length <= 3000s仅由数字组成
注意:本题与主站 93 题相同:https://leetcode-cn.com/problems/restore-ip-addresses/
2、解法1-嵌套循环
三层嵌套循环确定 IP 地址的4个子串,并检验其合法性
3、代码
var restoreIpAddresses = function (s) {
    const n = s.length;
    const valid = (str) => !((str.length > 1 && str[0] === '0') || +str > 255);
    const res = [];
    for (let i = 1; i < 4; i++) {
        if (n - i < 3 || n - i > 9) continue;
        for (let j = i + 1; j < i + 4; j++) {
            if (n - j < 2 || n - j > 6) continue;
            for (let k = j + 1; k < j + 4; k++) {
                if (n - k < 1 || n - k > 3) continue;
                const s1 = s.slice(0, i), s2 = s.slice(i, j);
                const s3 = s.slice(j, k), s4 = s.slice(k);
                if (valid(s1) && valid(s2) && valid(s3) && valid(s4)) res.push(`${s1}.${s2}.${s3}.${s4}`);
            }
        }
    }
    return res;
};
代码中的校验可以简化,但遍历次数会增加
var restoreIpAddresses = function (s) {
    const valid = (str) => !(str.length < 1 || str.length > 3 || (str.length > 1 && str[0] === '0') || +str > 255);
    const res = [];
    for (let i = 1; i < 4; i++) {
        for (let j = i + 1; j < i + 4; j++) {
            for (let k = j + 1; k < j + 4; k++) {
                const s1 = s.slice(0, i), s2 = s.slice(i, j);
                const s3 = s.slice(j, k), s4 = s.slice(k);
                if (valid(s1) && valid(s2) && valid(s3) && valid(s4)) res.push(`${s1}.${s2}.${s3}.${s4}`);
            }
        }
    }
    return res;
};
4、执行结果

5、解法2-回溯
使用回溯算法,在字符串 s 中选取3个索引作为选择路径(idxArr),其中索引值用于将字符串 s 分割成4个 IP 子串,以子串长度作为选择列表。
注意
- 选择列表(子串长度)只有 
1、2、3三种选择 - 为了代码简洁性,每次递归都传入不同引用的选择路径(
idxArr),这样可以省略撤销操作,但是消耗的内存会偏多 
6、代码
var restoreIpAddresses = function (s) {
    const n = s.length;
    const valid = (str) => !((str.length > 1 && str[0] === '0') || +str > 255);
    const res = [];
    function backtrace(idxArr) {
        const ln = idxArr.length, li = idxArr[ln - 1] || 0;
        if (n - li < 4 - ln || n - li > 3 * (4 - ln)) return;
        if (ln === 3) {
            const s1 = s.slice(0, idxArr[0]), s2 = s.slice(idxArr[0], idxArr[1]);
            const s3 = s.slice(idxArr[1], idxArr[2]), s4 = s.slice(idxArr[2]);
            if (valid(s1) && valid(s2) && valid(s3) && valid(s4)) res.push(`${s1}.${s2}.${s3}.${s4}`);
            return;
        }
        for (let i = 1; i <= 3; i++) backtrace([...idxArr, li + i]);
    }
    return backtrace([]), res;
};
7、执行结果
- 执行用时: 68 ms
 - 内存消耗: 43.4 MB